package lpcn.xbee;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Date;

import javax.swing.JButton;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import com.rapplogic.xbee.api.ApiId;
import com.rapplogic.xbee.api.PacketListener;
import com.rapplogic.xbee.api.XBeeAddress64;
import com.rapplogic.xbee.api.XBeeResponse;
import com.rapplogic.xbee.api.zigbee.ZNetRxResponse;
import com.rapplogic.xbee.api.zigbee.ZNetTxStatusResponse;
import com.rapplogic.xbee.util.ByteUtils;

public class SmartPowerUser
{
        public final static Logger log = Logger.getLogger(SmartPowerUser.class);

        /**
         * @param args
         */
        public static void main(String[] arguments)
        {
                PropertyConfigurator.configure("log4j.properties");
                UserGUI bf = new UserGUI();
        }
}

class UserGUI extends GeneralGUI implements ActionListener
{
        // CLASS VARIABLES
        String ip = "10.100.147.98";
        boolean connectingWiFi = false;
        boolean firstFileEver = true;

        // THREADS
        AddFilesToQueue tAddFilesToQueue = new AddFilesToQueue();
        SwitchingAlgorithm tSwitchingAlgorithm = new SwitchingAlgorithm();
        ShitPackets tShitPackets = new ShitPackets();
        
        // THREAD VARIABLES
        long counter_WiFi_QueueEmpty;
        long max_WiFi_QueueEmpty;
        long zigbeeQueueThreshold;
        boolean shittingPackets = false;

        // GUI Variables
        JButton requestBandwidthButton;
        JButton shitPacketsButton;
        boolean shitPackets;
        long amountPacketsShit;
        public Thread shitPacketsThread;
        JButton turnOffWiFiButton;
        JButton turnOnWiFiButton;
        JButton runAddFileScriptButton = new JButton("Run Add Files Script");

        // Constructor
        public UserGUI()
        {
                setTitle("Paty's Disco Jungl");

                requestBandwidthButton = new JButton("Request Bandwidth");
                shitPacketsButton = new JButton("Repeatedly Send Packets");
                turnOffWiFiButton = new JButton("turn off wifi");
                turnOnWiFiButton = new JButton("turn on wifi");

                requestBandwidthButton.addActionListener(this);
                shitPacketsButton.addActionListener(this);
                turnOffWiFiButton.addActionListener(this);
                turnOnWiFiButton.addActionListener(this);
                runAddFileScriptButton.addActionListener(this);

                pane.add(requestBandwidthButton);
                pane.add(shitPacketsButton);
                pane.add(turnOffWiFiButton);
                pane.add(turnOnWiFiButton);
                pane.add(runAddFileScriptButton);

                add(pane);
                setVisible(true);

                amountPacketsShit = 0;
                packetListener = new PacketListener()
                {
                        public void processResponse(XBeeResponse response)
                        {
                                //System.out.println("Received packet: " + response);
                                if (response.getApiId() == ApiId.ZNET_RX_RESPONSE)
                                {
                                        int[] payload;          // Stores the payload of the received packet
                                        ZNetRxResponse ZNetResponse = new ZNetRxResponse();             // Stores the ZNetRxResonse type of the received packet

                                        ZNetResponse = (ZNetRxResponse) response;       // Cast received packet to the correct type (ZNetRxResonse)
                                        payload = ZNetResponse.getData();                       // Store the data from the packet into the payload array

                                        switch(payload[0])      // Look at the status byte (first byte) of the payload
                                        {
                                        case '0':       // WIFI_REQUEST : Requests a Wi-Fi access point to connect to
                                                // not needed on the user node
                                                break;

                                        case '1':       // WIFI_INFO : Contains Wi-Fi access point information
                                                System.out.println("received connection info packet");
                                                String apInfoString = removeFirstChar(ByteUtils.toString(payload));     // Strip the status (first) byte from the payload
                                                String[] apInfo = apInfoString.split(",");      // Split the data into separate strings for file name and size
                                                String essid = apInfo[0];                                       // Store the essid
                                                String mode = apInfo[1];                                        // Store the mode
                                                String ap = apInfo[2];                                          // Store the ap
                                                connectWiFi(essid, mode, ap);           // Connect to the WiFi network
                                                break;

                                        case '2':       // WIFI_STOP : Informs Wi-Fi AP that the connection is closing
                                                // not needed on the user node
                                                break;

                                        case '3':       // FILE_TRANSFER_REQUEST : Contains file information
                                                String fileInfoString = removeFirstChar(ByteUtils.toString(payload));   // Strip the status (first) byte from the payload
                                                String[] fileInfo = fileInfoString.split(",");          // Split the data into separate strings for file name and size
                                                String fileName = fileInfo[0];                                          // Store the file name
                                                long fileSize = Long.parseLong(fileInfo[1]);            // Convert the file size string into a long and store it
                                                //System.out.println("Received file transfer request. Adding to queue: " + fileName);
                                                queueFiles.add(new FileInfo(fileName, fileSize, false));
                                                queueBytes = queueBytes + fileSize;
                                                //receiveFileTransferRequest(fileName, fileSize);               // Handle the file transfer request
                                                if(firstFileEver)
                                                {
                                                        firstFileEver = false;
                                                        scriptStartTime = System.currentTimeMillis();
                                                        System.out.println("Starting test simulation at time: " + scriptStartTime + " (" + new Date().toString() + ")");
                                                }
                                                break;

                                        case '4':       // FILE_DATA : Contains 70 bytes of file data 
                                                int[] dataInt = removeFirstArrayElement(payload);       // Strip the status (first) byte from the payload
                                                queueDataReceived.add(dataInt);         // Add the byte array to the received data queue
                                                break;

                                        case '5':       // INCOMING_FRIEND_TO_RECEIVE : Contains file information
                                                friendReadyToReceive = true;
                                                break;

                                        default:
                                                //System.out.println("Ignoring unexpected response: " + response);
                                                break;
                                        }
                                }
                        }
                };
        }

        // Event handler
        public void actionPerformed(ActionEvent event)
        {
                Object source = event.getSource();
                if(source == startButton)
                {
                        //System.out.println("Opening xbee interface");
                        openXBeeInterface(devicePort.getText(), 9600);
                        //System.out.println("Xbee interface opened");
                        xbee.setDestinationAddress64(new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x30, 0xc1, 0x47));
                        while(!xbee.xbee.isConnected())
                        {
                                // Do nothing until the xbee has been connected to
                        }
                        xbee.xbee.addPacketListener(packetListener);
                        //System.out.println("Added a packet listener");
                        stopNetworkManager();
                        tWiFiDataListener.stop();
                        turnOffWiFi();
                        tSwitchingAlgorithm.start();
                }
                else if(source == sendPacketButton)
                {
                        //ZNetTxStatusResponse response;
                        //ZNetTxStatusResponse response;
                        //int[] payload = new int[] { 'P', 'a', 't' };
                        //response = (ZNetTxStatusResponse) xbee.XBeeSendPacket(payload);
                        //System.out.println("Packet sent. Delivery status is: " + response.getDeliveryStatus());
                }
                else if(source == sendFileButton)
                {
                        File fileToSend = new File(fileLocation.getText());             // Look up the file on the hard drive
                        FileInfo fileInfo = new FileInfo(fileToSend.getName(), fileToSend.length(), true);      // Create a FileInfo object
                        sendFileTransferRequest(fileInfo);              // Send a file transfer request
                }
                else if(source == requestBandwidthButton)
                {
                        int[] payload = new int[] { '0' };
                        readyToSend = false;
                        xbee.XBeeSendPacket(payload);
                }
                else if(source == shitPacketsButton)
                {
                        if(shittingPackets)
                        {
                                tShitPackets.stop();
                                shittingPackets = false;
                        }
                        else
                        {
                                tShitPackets.start();
                                shittingPackets = true;
                        }
                        /*
                        shitPackets = true;
                        if(shitPacketsThread == null)
                        {
                                shitPacketsThread.start();
                        }
                        else
                                shitPackets = false;
                         */
                }
                else if(source == turnOffWiFiButton)
                {
                        turnOffWiFi();
                }
                else if(source == turnOnWiFiButton)
                {
                        connectWiFi("ulwireless", "Managed", "any");
                }
                else if(source == runAddFileScriptButton)
                {
                        scriptStartTime = System.currentTimeMillis();
                        tAddFilesToQueue.start();
                }
        }

        // Turn off the network manager
        public void stopNetworkManager()
        {
                try
                {            
                        Runtime rt = Runtime.getRuntime();
                        Process proc = rt.exec("sudo service network-manager stop");
                        int exitVal = proc.waitFor();
                        //System.out.println("Attempting to stop network manager: " + exitVal);
                }
                catch (Throwable t)
                {
                        t.printStackTrace();
                }
        }

        // Shut down the Wi-Fi card
        public void turnOffWiFi()
        {
                try
                {
                        connectedWiFi = false;  // Let the rest of the program know that Wi-Fi is NOT connected

                        //System.out.println("Is the socket connected? " + clientSocket.isConnected());
                        if(clientSocket.isConnected())          // If the socket is connected to anything
                        {
                                //clientObjectInputStream.close();
                                clientSocket.close();                   // Close the socket
                                //System.out.println("Closed the socket");
                        }

                        Runtime rt = Runtime.getRuntime();
                        //Process proc = rt.exec("sudo ifconfig wlan0 down");     // Shut down the Wi-Fi card
                        Process proc = rt.exec("sudo sh /home/pdesantis/Documents/workspace/xbee-api/src/lpcn/xbee/usbcontrol_OFF.sh");
                        int exitVal = proc.waitFor();           // Wait until it has been brought down
                }
                catch (Throwable t)
                {
                        t.printStackTrace();
                }
        }

        // Turn on the Wi-Fi card, wait until it associates, then open up a socket to the access point
        public void connectWiFi(String essid, String mode, String ap)
        {
                try
                {
                        Runtime rt = Runtime.getRuntime();
                        //Process proc = rt.exec("/home/pdesantis/Documents/workspace/xbee-api/src/lpcn/xbee/wireless.sh " + essid + " " + mode + " " + ap);
                        Process proc = rt.exec("sudo sh /home/pdesantis/Documents/workspace/xbee-api/src/lpcn/xbee/usbcontrol_ON.sh " + essid + " " + mode + " " + ap);
                        int exitVal = proc.waitFor();       // Wait until Wi-Fi has been brought up
                        if(exitVal == 0)
                        {
                                System.out.println("Attempting to connect to wifi: SUCCESS!");
                                System.out.println("Attempting to open socket...");
                                clientSocket = new Socket(ip, port);    // Connect to the access point
                                clientObjectOutputStream = new ObjectOutputStream(clientSocket.getOutputStream());
                                clientObjectInputStream = new ObjectInputStream(clientSocket.getInputStream());
                                tWiFiDataListener.start();                              // Start the WiFi data listener
                                System.out.println("Socket connection is " + clientSocket.isConnected());
                                connectedWiFi = true;                   // Let the rest of the program know that Wi-Fi is connected
                                connectingWiFi = false;                                 // TODO comment this
                                readyToSend = true;                             // Let the rest of the program know that it is okay to send data again
                        }
                        else
                        {
                                //System.out.println("attempting to connect to wifi: FAILURE!");
                        }
                }
                catch (IOException e)
                {
                        e.printStackTrace();
                }
                catch (InterruptedException e)
                {
                        e.printStackTrace();
                }
        }

        class AddFilesToQueue implements Runnable
        {
                private volatile Thread threadAddFilesToQueue;

                long ass;
                long file1;
                long file2;
                long file3;
                long file4;
                long file5;
                long file6;
                long file7;
                long file8;
                long file9;

                public void stop()
                {
                        threadAddFilesToQueue = null;
                }

                public void start()
                {
                        File file1temp = new File(FILEPATH + "user1");
                        File file2temp = new File(FILEPATH + "user2");
                        File file3temp = new File(FILEPATH + "user3");

                        File asstemp = new File(FILEPATH + "ass.jpg");
                        /*
                        File file1temp = new File(FILEPATH + "file1");
                        File file2temp = new File(FILEPATH + "file2");
                        File file3temp = new File(FILEPATH + "file3");
                         */
                        File file4temp = new File(FILEPATH + "file4");
                        File file5temp = new File(FILEPATH + "file5");
                        File file6temp = new File(FILEPATH + "file6");
                        File file7temp = new File(FILEPATH + "file7");
                        File file8temp = new File(FILEPATH + "file8");
                        File file9temp = new File(FILEPATH + "file9");

                        ass = asstemp.length();
                        file1 = file1temp.length();
                        file2 = file2temp.length();
                        file3 = file3temp.length();
                        file4 = file4temp.length();
                        file5 = file5temp.length();
                        file6 = file6temp.length();
                        file7 = file7temp.length();
                        file8 = file8temp.length();
                        file9 = file9temp.length();

                        threadAddFilesToQueue = new Thread(this);
                        threadAddFilesToQueue.start();
                }

                public void run()
                {
                        try
                        {
                                System.out.println("Starting add file thread");

                                Thread.sleep(3000);
                                sendFileTransferRequest(new FileInfo("user1", file1, true));
                                Thread.sleep(3000);
                                sendFileTransferRequest(new FileInfo("user2", file2, true));
                                sendFileTransferRequest(new FileInfo("user3", file3, true));
                        }
                        catch (InterruptedException e)
                        {
                                e.printStackTrace();
                        }
                }
        }

        class ShitPackets implements Runnable
        {
                private volatile Thread threadShitPackets;
                long start;
                long end;
                int packetsSent;

                public void stop()
                {
                        threadShitPackets = null;
                }

                public void start()
                {
                        threadShitPackets = new Thread(this);
                        threadShitPackets.start();
                }

                public void run()
                {
                        Thread thisThread = Thread.currentThread();
                        int[] payload = new int[] { '4', 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };
                        System.out.println("size of payload is :" + payload.length);
                        packetsSent = 0;
                        start = System.currentTimeMillis();
                        while(threadShitPackets == thisThread)
                        {
                                xbee.XBeeSendAsynchronousPacket(payload);
                                packetsSent++;
                        }
                        end = System.currentTimeMillis();
                        System.out.println("STOP STOP STOP Amoiunt of packets sent: " + packetsSent + " , total time: " + (end-start) + " milliseconds");
                }
        }
        
        // Takes packet from received from socket and adds it to the data queue
        // 
        class SwitchingAlgorithm implements Runnable
        {
                private volatile Thread threadSwitchingAlgorithm;

                void stop()
                {
                        threadSwitchingAlgorithm = null;
                }

                void start()
                {
                        threadSwitchingAlgorithm = new Thread(this);
                        threadSwitchingAlgorithm.start();
                }

                public void run()
                {
                        Thread thisThread = Thread.currentThread();
                        System.out.println("HEY PAT HEY PAT HEY PAT switching algorithm started");
                        
                        int[] payload;
                        counter_WiFi_QueueEmpty = 0;
                        
                        // move these to a radio button event listener thing
                        max_WiFi_QueueEmpty = 100;
                        zigbeeQueueThreshold = 6*1024;
                        
                        while(threadSwitchingAlgorithm == thisThread)
                        {
                                if(connectedWiFi)       // WIFI is on
                                {
                                        // Reset ZigBee counters
                                        
                                        // Check to see if the queue is empty
                                        if(queueBytes == 0)
                                        {
                                                counter_WiFi_QueueEmpty++;
                                        }
                                        else
                                        {
                                                counter_WiFi_QueueEmpty = 0;
                                        }
                                        
                                        if(counter_WiFi_QueueEmpty == max_WiFi_QueueEmpty)
                                        {
                                                System.out.println("Queue has been empty for long time. Turning off Wi-Fi");
                                                // Inform the AP that Wi-Fi is shutting off by sending a 'WIFI_STOP' packet
                                                // Send the packet, check whether it was delivered successfully or not, and if not, repeat sending it until it succeeds
                                                payload = new int[] { '2' };
                                                while(xbee.XBeeSendPacket(payload) != ZNetTxStatusResponse.DeliveryStatus.SUCCESS);
                                                
                                                // Turn off Wi-Fi
                                                turnOffWiFi();
                                                
                                                // Reset the WiFi counters
                                                counter_WiFi_QueueEmpty = 0;
                                        }
                                        
                                }
                                else    // ZIGBEE is on
                                {
                                        if(!connectingWiFi)
                                        {
                                                // Reset Wi-Fi counters
                                                counter_WiFi_QueueEmpty = 0;
                                                
                                                if(queueBytes >= zigbeeQueueThreshold)
                                                {
                                                        connectingWiFi = true;
                                                        System.out.println("Data Queue is too big. Switching to Wi-Fi");
                                                        // Request Wi-Fi
                                                        payload = new int[] { '0' };
                                                        readyToSend = false;
                                                        xbee.XBeeSendPacket(payload);
                                                }
                                        }
                                }
                                try
                                {
                                        Thread.sleep(100);      // Sleep for 0.1 seconds
                                }
                                catch (InterruptedException e)
                                {
                                        e.printStackTrace();
                                }
                        }
                }
        }
}